package de.bazinga.rotw.zombie;

import java.util.Vector;

import com.jme.intersection.PickData;
import com.jme.intersection.PickResults;
import com.jme.intersection.TrianglePickResults;
import com.jme.math.Ray;
import com.jme.math.Vector3f;
import com.jme.scene.Controller;
import com.jme.scene.Spatial;
import com.jmex.game.state.GameStateManager;
import com.jmex.terrain.TerrainBlock;

import de.bazinga.rotw.collision.CollidableObjects;
import de.bazinga.rotw.game.RotwGameState;
import de.bazinga.rotw.network.BasicSyncManager;
import de.bazinga.rotw.network.Server;
import de.bazinga.rotw.network.message.ZombieWaypointMessage;
import de.bazinga.rotw.person.Person;

public class ZombieController extends Controller {
	private static final long serialVersionUID = 1L;
	
	private Zombie zombie = null;
	
	private Waypoint currentWaypoint = null;
	
	private float speed = 2f;
	
	private Person personToFollow = null;
	

	private Ray ray = new Ray(new Vector3f(0,0,0),new Vector3f(0,-1,0));
	private PickResults results = new TrianglePickResults();
	private Vector<Spatial> ground = new Vector<Spatial>();
	
	
	public ZombieController(Zombie zombie, Waypoint startPoint) {
		this.zombie = zombie;
		this.currentWaypoint = startPoint;
	}

	
	
	public void update(float tpf) {
		if(personToFollow != null) {
			zombie.getLocalRotation().x = personToFollow.getLocalRotation().x;
			zombie.getLocalRotation().y = personToFollow.getLocalRotation().y;
			zombie.getLocalRotation().z = personToFollow.getLocalRotation().z;
			zombie.getLocalRotation().w = personToFollow.getLocalRotation().w;
			
			zombie.setLocalTranslation(personToFollow.getLocalTranslation().subtract(personToFollow.getLocalRotation().getRotationColumn(2).normalize()));
		}else{
			// Reached the Waypoint?
			float delta = 0.3f;
			if(currentWaypoint.getLocation().getX() > (zombie.getLocalTranslation().getX() - delta) &&
					currentWaypoint.getLocation().getX() < (zombie.getLocalTranslation().getX() + delta) &&
					currentWaypoint.getLocation().getZ() > (zombie.getLocalTranslation().getZ() - delta) &&
					currentWaypoint.getLocation().getZ() < (zombie.getLocalTranslation().getZ() + delta)) {
				
				// new Waypoint ( only if this is the server!)
				BasicSyncManager syncManager = 
					((RotwGameState)GameStateManager.getInstance().getChild("gameState")).getSyncManager();
				if(syncManager instanceof Server) {
					currentWaypoint = currentWaypoint.getNextWaypoint();
					
					// Look at the Waypoint
					Vector3f lookDirection = new Vector3f(currentWaypoint.getLocation().getX(), zombie.getLocalTranslation().getY(), currentWaypoint.getLocation().getZ());
					zombie.lookAt(lookDirection, Vector3f.UNIT_Y);
					
					// Send Message to Clients
					ZombieWaypointMessage zwm = new ZombieWaypointMessage();
					zwm.setZombieIndex(zombie.getZombieIndex());
					zwm.setWaypoint(currentWaypoint.getLocation());
					zwm.setZombieLocation(zombie.getLocalTranslation());
					((Server)syncManager).getServer().sendToAll(zwm);
				}
			}else{
			
			
				// Go to Waypoint
				Vector3f movement = new Vector3f(0,0,0);
				
				if((currentWaypoint.getLocation().getX() - zombie.getLocalTranslation().getX()) > delta) {
					movement.setX(tpf * speed);
				}else if((currentWaypoint.getLocation().getX() - zombie.getLocalTranslation().getX()) < -delta) {
					movement.setX(-tpf * speed);
				}
				
		
				/*if((currentWaypoint.getLocation().getY() - zombie.getLocalTranslation().getY()) > delta) {
					movement.setY(tpf * speed);
				}else if((currentWaypoint.getLocation().getY() - zombie.getLocalTranslation().getY()) < -delta) {
					movement.setY(-tpf * speed);
				}*/
		
				if((currentWaypoint.getLocation().getZ() - zombie.getLocalTranslation().getZ()) > delta) {
					movement.setZ(tpf * speed);
				}else if((currentWaypoint.getLocation().getZ() - zombie.getLocalTranslation().getZ()) < -delta) {
					movement.setZ(-tpf * speed);
				}
				
				
				zombie.setLocalTranslation(
						zombie.getLocalTranslation().add(movement));
				
				
				// Set Zombie on the Ground
				setOnGround();
			}
		}
	}
	
	public void setCurrentWaypoint(Waypoint w) {
		this.currentWaypoint = w;
		
		// Look at the Waypoint
		Vector3f lookDirection = new Vector3f(currentWaypoint.getLocation().getX(), zombie.getLocalTranslation().getY(), currentWaypoint.getLocation().getZ());
		zombie.lookAt(lookDirection, Vector3f.UNIT_Y);
		
	}
	
	
	public void setOnGround() {		
		// Get New Location
		Vector3f origin = zombie.getLocalTranslation().clone();
		origin.addLocal(new Vector3f(0,1.5f,0));
		
		ray.setOrigin(origin);
		
		results.clear();
		results.setCheckDistance(true);
		
		
		float distance = -5;
		for(int i=0;i<ground.size();i++) {
			ground.get(i).findPick(ray,results);
			
			//check if we hit something
			if(results.getNumber() > 0) {
			   PickData closest = results.getPickData(0);

			   if(distance < 0 || distance > closest.getDistance()) {
				   distance = closest.getDistance();
			   }
			}
		}
		
		if(distance > 0) {
			zombie.getLocalTranslation().setY(
					origin.addLocal(new Vector3f(0,-distance,0)).getY());
		}
	}
	
	public void resetGroundVector() {
		ground.clear();
		TerrainBlock tb = ((RotwGameState)GameStateManager.getInstance().getChild("gameState")).getTerrainBlock();
		if(tb != null) ground.add(tb);
		
		if(CollidableObjects.getInstance().getObjects("ground") != null) {
			ground.addAll(CollidableObjects.getInstance().getObjects("ground"));
		}
		if(CollidableObjects.getInstance().getObjects("wall") != null) {
			ground.addAll(CollidableObjects.getInstance().getObjects("wall"));
		}
	}



	public void setPersonToFollow(Person personToFollow) {
		this.personToFollow = personToFollow;
	}



	public Person getPersonToFollow() {
		return personToFollow;
	}
}